#!/bin/bash

function build_completion() {
    local HOSTS=("arm32" "arm32hf" "arm64" "host")
    local COMPILERS=("gcc" "clang")
    local BUILD_VARIANTS=("debug" "fastverify" "release")
    local OPTIONS=("asan" "ubsan" "tsan")
    local MAKE_TARGETS="ark ark_aot ark_asm ark_disasm es2panda panda_bins clang-force-format tests"

    local ARG_IDX=1 #skip command
    local TARGET=""
    local MAKE_TARGET=""
    while [ $ARG_IDX -lt $COMP_CWORD ]; do
        if [[ ! ${COMP_WORDS[$ARG_IDX]} =~ ^-.* ]]; then
            if [ -z "$TARGET" ]; then
                TARGET=${COMP_WORDS[$ARG_IDX]}
            else
                MAKE_TARGET=${COMP_WORDS[$ARG_IDX]}
            fi
        fi
        ARG_IDX=$((ARG_IDX+1))
    done
    if [ -z "$TARGET" ]; then
        local STR=""
        for host in "${HOSTS[@]}"; do
            for compiler in "${COMPILERS[@]}"; do
                for var in "${BUILD_VARIANTS[@]}"; do
                    STR="$STR $host-$compiler-$var"
                    for opt in "${OPTIONS[@]}"; do
                        STR="$STR $host-$compiler-$var-$opt"
                    done
                done
            done
        done
        COMPREPLY=($(compgen -W "$STR" "${COMP_WORDS[$ARG_IDX]}"))
        return
    fi
    if [ -z "$MAKE_TARGET" ]; then
        COMPREPLY=($(compgen -W "$MAKE_TARGETS" "${COMP_WORDS[$ARG_IDX]}"))
        return
    fi
}
complete -F build_completion build

function build() {
    MAKE_TARGET=""
    CLEAN_BUILD=false
    TARGET=host
    NUM_THREADS=""
    #CMAKE_OPTIONS="-DPANDA_WITH_ACCORD=false -DPANDA_WITH_CANGJIE=false -DPANDA_WITH_JAVA=false"
    CMAKE_OPTIONS="-DPANDA_ENABLE_CCACHE=OFF -DPANDA_WITH_ECMASCRIPT=off"
    MAKE="ninja"

    while [ ! -z "$1" ]; do
        if [[ $1 =~ ^-.* ]]; then
            case "$1" in
                "-c")
                    CLEAN_BUILD=true
                    ;;
                "-m")
                    MAKE="make"
                    ;;
                "-j")
                    shift
                    NUM_THREADS=$1
                    ;;
                -D*)
                    CMAKE_OPTIONS="$CMAKE_OPTIONS $1"
                    ;;
                *)
                    echo "Unknown option $1"
                    return
                    ;;
            esac
        else
            TARGET=$1
            shift
            if [ ! -z "$1" ]; then
                MAKE_TARGET=$1
            fi
            break
        fi
        shift
    done

    if [ "$MAKE" == "make" ]; then
        if [ -z "$NUM_THREADS" ]; then
            NUM_THREADS=10
        fi
    elif [ "$MAKE" == "ninja" ]; then
        CMAKE_OPTIONS="-GNinja $CMAKE_OPTIONS"
    fi
    if [ ! -z "$NUM_THREADS" ]; then
        MAKE="$MAKE -j$NUM_THREADS"
    fi
    if [ -z "$TARGET" ]; then
        echo "Target is unspecified"
        return
    fi
    IFS='-' read -ra BUILD_VARIANTS <<< "$TARGET"
    if [ ${#BUILD_VARIANTS[@]} -eq 0 ]; then
        echo "Invalid target"
        return
    fi
    if [[ ! "${CMAKE_OPTIONS}" =~ "-DPANDA_ETS_INTEROP_JS" ]]; then
        if [[ ${BUILD_VARIANTS[0]} == "host" || ${BUILD_VARIANTS[0]} == "ohos" ]]; then
            CMAKE_OPTIONS="$CMAKE_OPTIONS -DPANDA_ETS_INTEROP_JS=true"
        fi
    fi

    CMAKE_CMD="cmake $CMAKE_OPTIONS"
    BUILD_DIR="build"

    case "${BUILD_VARIANTS[0]}" in
    "arm"|"arm32")
        TARGET="arm32"
        BUILD_DIR="$BUILD_DIR/$TARGET"
        if [[ "${BUILD_VARIANTS[@]}" =~ "gcc" ]]; then
            CMAKE_CMD="$CMAKE_CMD -DCMAKE_TOOLCHAIN_FILE=../../cmake/toolchain/cross-gcc-default-qemu-arm-linux-gnueabi.cmake"
            BUILD_DIR="$BUILD_DIR-gcc"
        else
            CMAKE_CMD="$CMAKE_CMD -DCMAKE_TOOLCHAIN_FILE=../../cmake/toolchain/cross-clang-9-qemu-arm-linux-gnueabihf.cmake"
            BUILD_DIR="$BUILD_DIR-clang"
        fi
        ;;
    "armhf"|"arm32hf")
        TARGET="arm32hf"
        BUILD_DIR="$BUILD_DIR/$TARGET"
        if [[ "${BUILD_VARIANTS[@]}" =~ "gcc" ]]; then
            CMAKE_CMD="$CMAKE_CMD -DCMAKE_TOOLCHAIN_FILE=../../cmake/toolchain/cross-gcc-default-qemu-arm-linux-gnueabihf.cmake"
            BUILD_DIR="$BUILD_DIR-gcc"
        else
            CMAKE_CMD="$CMAKE_CMD -DCMAKE_TOOLCHAIN_FILE=../../cmake/toolchain/cross-clang-9-qemu-arm-linux-gnueabihf.cmake"
            BUILD_DIR="$BUILD_DIR-clang"
        fi
        ;;
    "aarch64"|"arm64")
        TARGET="arm64"
        BUILD_DIR="$BUILD_DIR/$TARGET"
        if [[ "${BUILD_VARIANTS[@]}" =~ "gcc" ]]; then
            CMAKE_CMD="$CMAKE_CMD -DCMAKE_TOOLCHAIN_FILE=../../cmake/toolchain/cross-gcc-default-qemu-aarch64.cmake"
            BUILD_DIR="$BUILD_DIR-gcc"
        else
            CMAKE_CMD="$CMAKE_CMD -DCMAKE_TOOLCHAIN_FILE=../../cmake/toolchain/cross-clang-9-qemu-aarch64.cmake"
            BUILD_DIR="$BUILD_DIR-clang"
        fi
        ;;
    "ohos")
        TARGET="ohos"
        BUILD_DIR="$BUILD_DIR/$TARGET"
        CMAKE_CMD="$CMAKE_CMD -DCMAKE_TOOLCHAIN_FILE=../../cmake/toolchain/cross-ohos-musl-aarch64.cmake \
            -DTOOLCHAIN_SYSROOT=/usr/local/ohos-sdk/native/sysroot -DTOOLCHAIN_CLANG_ROOT=/usr/local/ohos-sdk/native/llvm \
            -DPANDA_WITH_OHOS_NAPI=ON"
        ;;
    *)
        TARGET="host"
        BUILD_DIR="$BUILD_DIR/$TARGET"
        if [[ "${BUILD_VARIANTS[@]}" =~ "gcc" ]]; then
            CMAKE_CMD="$CMAKE_CMD -DCMAKE_TOOLCHAIN_FILE=../../cmake/toolchain/host_gcc_11.cmake"
            BUILD_DIR="$BUILD_DIR-gcc"
        else
            CMAKE_CMD="$CMAKE_CMD -DCMAKE_TOOLCHAIN_FILE=../../cmake/toolchain/host_clang_14.cmake"
            BUILD_DIR="$BUILD_DIR-clang"
        fi
        ;;
    esac

    if [[ "${BUILD_VARIANTS[@]}" =~ "reldebug" ]]; then
        CMAKE_CMD="$CMAKE_CMD -DCMAKE_BUILD_TYPE=RelWithDebInfo"
        BUILD_DIR="$BUILD_DIR-reldebug"
    else
        if [[ "${BUILD_VARIANTS[@]}" =~ "release" ]]; then
            CMAKE_CMD="$CMAKE_CMD -DCMAKE_BUILD_TYPE=Release"
            BUILD_DIR="$BUILD_DIR-release"
        elif [[ "${BUILD_VARIANTS[@]}" =~ "fastverify" ]]; then
            CMAKE_CMD="$CMAKE_CMD -DCMAKE_BUILD_TYPE=FastVerify"
            BUILD_DIR="$BUILD_DIR-fastverify"
        elif [[ "${BUILD_VARIANTS[@]}" =~ "debug" ]]; then
            CMAKE_CMD="$CMAKE_CMD -DCMAKE_BUILD_TYPE=Debug"
            BUILD_DIR="$BUILD_DIR-debug"
        fi
    fi
    XSAN=false
    if [[ "${BUILD_VARIANTS[@]}" =~ "asan" ]]; then
        CMAKE_CMD="$CMAKE_CMD -DPANDA_ENABLE_ADDRESS_SANITIZER=true"
        BUILD_DIR="$BUILD_DIR-asan"
        XSAN=true
    fi
    if [[ "${BUILD_VARIANTS[@]}" =~ "ubsan" ]]; then
        CMAKE_CMD="$CMAKE_CMD -DPANDA_ENABLE_UNDEFINED_BEHAVIOUR_SANITIZER=true"
        BUILD_DIR="$BUILD_DIR-ubsan"
        XSAN=true
    fi
    if [[ "${BUILD_VARIANTS[@]}" =~ "tsan" ]]; then
        CMAKE_CMD="$CMAKE_CMD -DPANDA_ENABLE_THREAD_SANITIZER=true"
        BUILD_DIR="$BUILD_DIR-tsan"
        if [[ "${BUILD_VARIANTS[@]}" =~ "clang" ]]; then
            CMAKE_CMD="$CMAKE_CMD -DPANDA_THREAD_SAFETY=true"
        fi
        XSAN=true
    fi
    if [ XSAN == true ]; then
        export LSAN_OPTIONS=detect_leaks=0
    fi
    CMAKE_CMD="$CMAKE_CMD ../.."

    echo "Build in $BUILD_DIR"
    if [ ! -d "$BUILD_DIR" ]; then
        CLEAN_BUILD=true
    elif [ $CLEAN_BUILD == true ]; then
        rm -rf "$BUILD_DIR"
    fi
    trap popd SIGINT SIGTERM
    pushd .
    mkdir -p "$BUILD_DIR"
    cd $BUILD_DIR

    RUN_CMAKE=false
    if [ $CLEAN_BUILD == true -o ! -f cmake.cmd ]; then
        RUN_CMAKE=true
    else
        OLD_CMAKE_CMD=`cat cmake.cmd`
        if [ "$OLD_CMAKE_CMD" != "$CMAKE_CMD" ]; then
            RUN_CMAKE=true
        fi
    fi
    if [ $RUN_CMAKE == true ]; then
        echo "$CMAKE_CMD"
        $CMAKE_CMD
        echo "$CMAKE_CMD" > cmake.cmd
    fi
    $MAKE $MAKE_TARGET
    popd
}
